#include "cstdlib"
#include "FileSystem.h"
#include "File.h"
#include "Request.h"
#include <map>
#include <iostream>


FileSystem::FileSystem( Logger& Err, Logger& LOG ):LOGGER( LOG ), ERRLOGGER( Err ), cache( LOGGER )
{

	 LOGGER<<"Starting File System.................\n";

};
bool FileSystem::RetreiveFile( int FileID  )
{


	LOGGER<<"FileSystem::RetreiveFile: Entering \n";
	//Look in the cache first:
	File* pFile = cache.Retreive( FileID );
	if( pFile != NULL )
	{
		//File found in cache , return true.
		return true;
	}


	//Else, look in the file system map.
	std::map< int, File* >::iterator mapit;
	mapit = m_Files.find( FileID );

	if( mapit != m_Files.end() )
	{
		LOGGER<<"FileSystem::RetreiveFile retrieving File with ID "<<FileID<<"\n";
		cache.Insert( (mapit)->second );
	}
	else
	{
		ERRLOGGER<<"ERROR: FileSystem::RetreiveFile : file with ID"<< FileID << "Not found \n" ;
		return false;
	}

	return true;
};
bool FileSystem::StoreFile( int FileID )
{
	std::map< int, File* >::iterator mapit;
	mapit = m_Files.find( FileID );

	if( mapit != m_Files.end() )
	{
		LOGGER<<"FileSystem::StoreFile Storing File with ID "<<FileID<<"\n";
		cache.Insert( (mapit)->second );
	}
	else
	{
		ERRLOGGER<<"ERROR: FileSystem::StoreFile : file with ID"<< FileID << "Not found \n" ;
	}

	return true;
};
bool FileSystem::AuthenticateFile( int ID, std::string IPAddress)
{
	std::map< int, File* >::iterator mapit;
	mapit = m_Files.find( ID );

	if( mapit != m_Files.end() )
	{
		return  mapit->second->Authenticate( IPAddress ) ;//( IPAddress ==  mapit->second->m_IPAddress );
	}
	else
	{
		ERRLOGGER<<"ERROR: FileSystem::AuthenticateFile: file with ID"<< ID << "Not found \n" ;
	}

	return false;
};
bool FileSystem::CreateFile( Request* pRequest )
{

	File* pFile = new File( pRequest->m_IPAddress,
							pRequest->m_strURL,
							pRequest->m_strFileName,
							pRequest->m_TimeStamp,
							pRequest->m_NewID );

	LOGGER<<"FileSystem::CreateFile "<<"Adding <"<<pRequest->m_NewID<<">"<<pFile<<"\n";
	m_Files.insert( std::pair< int,File*>( pRequest->m_NewID, pFile ) );
	pRequest->m_FileIDs.push_back( pRequest->m_NewID );
	cache.Insert( pFile );

	return true;
};
bool FileSystem::CreateFile( std::string Name, std::string Path, bool isSysFile , int ID )
{

	//This override is called when creating the default system files ( the ones in the resolve file ).
	File* pFile = new File( "0.0.0.0",
							Path,
							Name,
							"10:34:14 11/5/1980",//Assume all system files were created at this time.
							ID ,
							isSysFile );

	LOGGER<<"FileSystem::CreateFile "<<"Adding <"<<ID<<">"<<pFile<<" as System file \n";

	m_Files.insert( std::pair< int,File*>( ID, pFile ) );
	cache.Insert( pFile );

	return true;
};
//Update time stamps for already created files
bool FileSystem::UpdateFiles( Request* pRequest )
{
	std::vector<int>::iterator it;
	std::map< int, File* >::iterator mapit;

	for( it = pRequest->m_FileIDs.begin() ; it < pRequest->m_FileIDs.end() ; it++ )
	{
		mapit = m_Files.find( *(it) );
		if( mapit != m_Files.end() )
		{
			(mapit)->second->UpdateFile( pRequest->m_TimeStamp  );
			cache.Insert( mapit->second );
		}
	}
	return true;
};
bool FileSystem::IsUserDirectory( std::string path )
{
	//User Managed directory must starts with /user
	return ( path.find_first_of( c_strUserDire ) != std::string::npos );
};
bool FileSystem::HandleStore( Request* pRequest )
{
	bool success = true;

	std::vector<int>::iterator it = pRequest->m_FileIDs.begin();
	for( ; it < pRequest->m_FileIDs.end(); it++  )
	{
		success &= StoreFile( *(it) );
	}

	return success;
}
bool FileSystem::HandleRetrv( Request* pRequest )
{

	bool success = true;

	std::vector<int>::iterator it = pRequest->m_FileIDs.begin();
	for( ; it < pRequest->m_FileIDs.end(); it++  )
	{
		success &= RetreiveFile( *(it) );
		UpdateFiles( pRequest );//Update time stamp for the file.
	}

	return success;
}
Cache::Cache( Logger& log ):LOGGER( log )
{

	m_iSmallestElement	= -1 ;
	m_Index				= -1;
};
int Cache::FindOldest()
{
	int oldest = 0;

	for( int idx = 0; idx <= m_Index ; idx++ )
	{
		//if( m_Cache[ oldest ]->m_TimeStamp > m_Cache[ idx ]->m_TimeStamp )
		double diff = difftime(m_Cache[ oldest ]->m_TimeStamp, m_Cache[ idx ]->m_TimeStamp );
		if( diff > 0 )
		{
			oldest = idx;
		}
	}
	return oldest;
}
void Cache::Insert( File* pFile  )
{

	if( pFile->m_bCached )
	{
		return;
	}

	if( -1 == m_Index )
	{
		//Cache Empty.
		m_Index = 0;
		m_Cache[ m_Index ] = pFile;
	}
	else
	{
		if( m_Index == ( c_CachSize - 1 )  )//Cache full
		{
			int Idx						= FindOldest();
			LOGGER<<"Cache::Insert removing "<<m_Cache[ Idx ]->m_Name <<" from cache \n";
			m_Cache[ Idx ]->m_bCached	= false;
			m_Cache[ Idx ]				= pFile;
		}
		else
		{
			m_Cache[ ++m_Index ]	= pFile;
		}
	}

	LOGGER<<"Cache::Insert Inserting "<<pFile->m_Name <<" into cache \n";
	pFile->m_bCached	= true;
};
File* Cache::Retreive( int ID )
{
	File* pFile = NULL;
	for( int Idx = 0 ; Idx < c_CachSize; Idx++ )
	{
		if( m_Cache[ Idx ]->m_ID == ID )
		{
			LOGGER<< "Cache::Retrieve Cache Hit for file ";
			pFile = m_Cache[ Idx ];
			LOGGER<<pFile->m_Name << "\n" ;
			break;
		}
	}

	if( pFile == NULL )
	{
		LOGGER<< "Cache::Retrieve Cache Miss for file with ID: "<< ID << "\n" ;
	}

	return pFile;
};


